/*************************************************************************
	> File Name: decrypt.cpp
	> Author: xuwenlong
	> Mail: myxuan475@126.com 
	> Created Time: 2018年10月17日 星期三 14时25分23秒
 ************************************************************************/
#include "decrypt.h"
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <openssl/ssl.h>

/* sz_in_buff 输入字符串
*  key 为密钥
*  iv为偏移量
*  sz_out_buff 加密输出字符串
*/
int aes_encrypt_pkcs5pading(unsigned char *sz_in_buff, int sz_in_len, unsigned char *key,unsigned char *iv, unsigned char *sz_out_buff)
{
    EVP_CIPHER_CTX ctx;

    int isSuccess = 0;
    int outl = 0;

    EVP_CIPHER_CTX_init(&ctx);

    EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
    EVP_CIPHER_CTX_set_padding(&ctx, 1);

    isSuccess = EVP_EncryptUpdate(&ctx, sz_out_buff, &outl,sz_in_buff,sz_in_len);
    if(!isSuccess)
    {
        printf("EVP_EncryptUpdate() failed");
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }
    int last = 0;
    isSuccess = EVP_EncryptFinal_ex(&ctx,sz_out_buff + outl,&last);
    if(!isSuccess)
    {
        printf("EVP_EncryptUpdate() failed");
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }
    outl += last;
    EVP_CIPHER_CTX_cleanup(&ctx);
    return outl;
}

/* sz_in_buff 输入解密字符串
*  key 为密钥
*  iv为偏移量
*  sz_out_buff 输出解密后字符串
*/
int aes_decrypt_pkcs5pading(unsigned char *sz_in_buff, int sz_in_length, unsigned char *key,unsigned char *iv, unsigned char *sz_out_buff)
{
    int outl = 0;
    int isSuccess;

    EVP_CIPHER_CTX ctx;

    //初始化ctx，加密算法初始化
    EVP_CIPHER_CTX_init(&ctx);
    isSuccess = EVP_DecryptInit_ex(&ctx,EVP_aes_128_cbc(),NULL,key,iv);
    if(!isSuccess) {
        printf("EVP_DecryptInit_ex() failed");
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }

    //解密数据
    EVP_CIPHER_CTX_set_padding(&ctx,1);

    isSuccess = EVP_DecryptUpdate(&ctx, sz_out_buff, &outl,sz_in_buff,sz_in_length);

    if (!isSuccess) {
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }
    int last = 0;
    isSuccess = EVP_DecryptFinal_ex(&ctx, sz_out_buff + outl, &last);
    if (!isSuccess) {
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }
    outl += last;
    EVP_CIPHER_CTX_cleanup(&ctx);
    return outl;
}
//---------------------
//作者：viewsky11
//来源：CSDN
//原文：https://blog.csdn.net/viewsky11/article/details/78983234
//版权声明：本文为博主原创文章，转载请附上博文链接！

UtilsCrypt::UtilsCrypt(const std::string &key_, MODE mode_)
{
    private_key = key_;
    mode = mode_;
}

int UtilsCrypt::Encrypt(const char *data,int len,char *&out)
{
    unsigned char iv[AES_BLOCK_SIZE];
//    AES_KEY en_key;
    int length = 0;

    //初始化
    memset(iv,0,AES_BLOCK_SIZE);
    memcpy(iv,private_key.c_str(),AES_BLOCK_SIZE);
    length = ((len + AES_BLOCK_SIZE-1)/AES_BLOCK_SIZE)*AES_BLOCK_SIZE;

    out = (char *)malloc(length);

    memset(out,0,length);
    return aes_encrypt_pkcs5pading((unsigned char*)data,len,iv,iv,(unsigned char*)out);
#if 0
//    des_setparity(key);
    length = ((len + AES_BLOCK_SIZE-1)/AES_BLOCK_SIZE)*AES_BLOCK_SIZE;

    std::string str((char*)data,len);
    for (int i = len;i < length;i++) {
        str+='\n';
    }

    out = (char *)malloc(length);

    memset(out,0,length);

    AES_set_encrypt_key((unsigned char*)private_key.c_str(),AES_BLOCK_SIZE*8,&en_key);

    if (mode == cbc)
        AES_cbc_encrypt((unsigned char*)str.c_str(),(unsigned char*)out,length,&en_key,iv,AES_ENCRYPT);
    else if (mode == ecb)
        AES_ecb_encrypt((unsigned char*)str.c_str(),(unsigned char*)out,&en_key,AES_ENCRYPT);

    return length;
#endif
}

std::string UtilsCrypt::Decrypt(const std::string &str)
{
    char *out;
    unsigned char iv[AES_BLOCK_SIZE];
    int length = 0;

    //初始化
    memset(iv,0,AES_BLOCK_SIZE);
    memcpy(iv,private_key.c_str(),AES_BLOCK_SIZE);
    length = ((str.length() + AES_BLOCK_SIZE-1)/AES_BLOCK_SIZE)*AES_BLOCK_SIZE;
    out = (char *)malloc(length);
    int len = aes_decrypt_pkcs5pading((unsigned char*)str.c_str(),str.length(),iv,iv,(unsigned char *)out);
    if (len <= 0)
        return "";
    std::string strout(out,len);
    free(out);
    return strout;
}

std::string UtilsCrypt::Encrypt(const unsigned char *data, int len)
{
    unsigned char *result;
    unsigned char iv[AES_BLOCK_SIZE];
    AES_KEY en_key;
    int length = 0;

    //初始化
    memset(iv,0,AES_BLOCK_SIZE);
    memcpy(iv,private_key.c_str(),AES_BLOCK_SIZE);
//    des_setparity(key);
    length = ((len + AES_BLOCK_SIZE-1)/AES_BLOCK_SIZE)*AES_BLOCK_SIZE;

    std::string str((char*)data,len);
    for (int i = len;i < length;i++) {
        str+='\n';
    }

    result = (unsigned char *)malloc(length);

    memset(result,0,length);

    AES_set_encrypt_key((unsigned char*)private_key.c_str(),AES_BLOCK_SIZE*8,&en_key);

    if (mode == cbc)
        AES_cbc_encrypt((unsigned char*)str.c_str(),result,length,&en_key,iv,AES_ENCRYPT);
    else if (mode == ecb)
        AES_ecb_encrypt((unsigned char*)str.c_str(),result,&en_key,AES_ENCRYPT);

    std::string encrypt;
    encrypt.append((char *)result,length);
    delete result;
    return encrypt;
}

int UtilsCrypt::Decrypt(const char *data, int len,char* &out)
{
    unsigned char iv[AES_BLOCK_SIZE];
    AES_KEY en_key;
    int olen = 0;

    //初始化
    memset(iv,0,AES_BLOCK_SIZE);
    memcpy(iv,private_key.c_str(),AES_BLOCK_SIZE);
//    des_setparity(key);
    olen = ((len + AES_BLOCK_SIZE-1)/AES_BLOCK_SIZE)*AES_BLOCK_SIZE;
    out = (char *)malloc(olen+1);

    memset(out,0,olen);

    AES_set_decrypt_key((unsigned char *)private_key.c_str(),AES_BLOCK_SIZE*8,&en_key);

    if (mode == cbc)
        AES_cbc_encrypt((unsigned char*)data,(unsigned char*)out,len,&en_key,iv,AES_DECRYPT);
    else if (mode == ecb)
        AES_ecb_encrypt((unsigned char*)data,(unsigned char*)out,&en_key,AES_DECRYPT);
    return olen;
}
